package  com.openrange.dukes
{
	import flash.geom.Point;
	import org.flixel.*;
	
	public class Terrain 
	{
		[Embed(source = "../../../../assets/terrain_tiles.png")] protected var ImgTech:Class;
		protected var _terrain:FlxGroup;
		
		//if we do change our minds later can be easily updates
		public var offsetX:int = 14;
		public var offsetY:int = 14;		
		public var tileWidth:int = 37;
		public var tileHeight:int = 37;
		public var mapWidth:int = 20;
		public var mapHeight:int = 20;
		
		
		//used to stamp the different terrains to the tiles
		private var stamp:FlxSprite;
		
		
		//paramter for terrain generation
		private var maxDesertHeight:int = 5;
		private var minDesertHeight:int = 3;
		
		
		public function Terrain(fs:FlxState) 
		{
			
			_terrain = new FlxGroup();
			
			generateLevel();
			
			fs.add(_terrain);
			
		}
		
		public function getScreenPositionX(tileX:int):int
		{
			var screenX:int = tileX * tileWidth;
			screenX = screenX + offsetX;
			return screenX;
		}
		public function getScreenPositionY(tileY:int):int
		{
			var screenY:int = tileY * tileHeight;
			screenY = screenY + offsetY;
			return screenY;
		}
		
		protected function generateLevel():void //This whole thing is a placeholder for a real terrain generation function.
		{
			
			//load tiles, use this FlxSprite to stamp the individual tiles with the correct terrain
			stamp = new FlxSprite().loadGraphic(ImgTech);
			
			
			var bx:uint = offsetX;
			var by:uint = offsetY;
			
			
			///////
			//base grassland
			for (var y:uint = 0; y < mapHeight; y++)
			{
				bx = offsetX;
				for (var x:uint = 0; x < mapWidth; x++)
				{
					_terrain.add(new Tile(bx, by, tileWidth, tileHeight, stamp, Tile.GRASS));
					bx = bx+tileWidth;
				}
				
				by = by+tileHeight;
			}

			///////
			//Randomly add forests, completely random, cover roughly 20 tiles (which is 5% of 20x20 map) add 7% to allow for them being replaced
			for (y = 0; y < mapHeight; y++)
			{
				for (x = 0; x < mapWidth; x++)
				{			
					if (Math.random() < 0.07)
					{
						var t:int = getTile(x, y);
						//just incase tile is not found
						if (t != -1)
						{
							_terrain.members[t].setTileType(stamp, Tile.FOREST);
						}
					}
				}
			}			
			
			///////
			//Add 2-3 lakes. Groups of water tiles ranging from 7-20 tiles
			var noLakes:int = 2 + Math.round(Math.random());
			for (var ct:int = 0; ct < noLakes; ct++)
			{
				//pick seed location
				x = Math.round(Math.random() * mapWidth);
				y = Math.round(Math.random() * mapHeight);
				createRegion(x, y, 0, Tile.WATER, 0.7,4);
			}
			
			///////
			//Add a line of desert down the center of the map (horizontally)
			var desY:int = (mapHeight / 2) + Math.round((Math.random()*4)-2);
			var desHeight:int = 3;
			for (x = 0; x < mapWidth; x++)
			{
				//change the positioning of the desert on the y axis slightly
				var delta:int = Math.round((Math.random() * 2) - 1);
				desY = desY + delta;
				//randomly fluctuate height of desert
				desHeight = desHeight + Math.round((Math.random() * 2) - 1);
				if (desHeight > maxDesertHeight){ desHeight = maxDesertHeight; }
				if (desHeight < minDesertHeight){ desHeight = minDesertHeight; }
				for (y = desY; y < desY + desHeight; y++)
				{
					t = getTile(x, y);
					if (t != -1)
					{
						_terrain.members[t].setTileType(stamp,Tile.DESERT);
					}					
				}
			}
			
			///////
			//Add 6-8 mountain areas. Groups of mountain tiles ranging from 1-8 tiles
			var noMountains:int = 6 + Math.round(Math.random()*2);
			for (ct = 0; ct < noMountains; ct++)
			{
				//pick seed location
				x = Math.round(Math.random() * mapWidth);
				y = Math.round(Math.random() * mapHeight);
				if (Math.random() < 0.6) { createRegion(x, y, 0, Tile.MOUNTAIN, 0.2,2); }
				else { createRegion(x, y, 0, Tile.MOUNTAIN, 0, 1);} 				
			}			
			

		}
		
		/**
		 * Return a random position, which can serve as a starting position for a duke. Ensures the duke can move from the position.
		 */
		public function getValidStartingPosition():Point
		{
			var p:Point = new Point(0, 0);
			while (1==1)
			{
				p.x = Math.round(Math.random() * (mapWidth-1));
				p.y = Math.round(Math.random() * (mapHeight-1));
				//check duke can get out
				for (var ct:int = 0; ct < 4; ct++)
				{
					var t:int;
					if (ct == 0) t = getTile(p.x + 1, p.y);
					if (ct == 1) t = getTile(p.x - 1, p.y);
					if (ct == 2) t = getTile(p.x, p.y + 1);
					if (ct == 3) t = getTile(p.x, p.y - 1);
					
					if (t == -1)
					{
						continue;
					}
					
					if (_terrain.members[t].getTileType() != Tile.WATER)
					{
						return p;
					}
				}
			}
			return null;
		}
		
		/**
		 * Recusive function, add lake to tile [tileCoordinatesX,tileCoordinatesY], then add lake to neighbours if lake isnt to big

		 * @param	tileCoordinatesX		The X-coordinate in tile space (not in pixels!).
		 * @param	tileCoordinatesY		The Y-coordinate in tile space (not in pixels!).
		 * @param	distFromSeed			The distance from the seed (ie size of lake) regulates the probability of adding water to neighbouring tiles
		 * @param	type					Terrain type of region
		 * @param 	size					Regulates the growth of the region. each tile from the seed position the prob of becoming region is size/dist
		 */
		private function createRegion(tileCoordinatesX:int, tileCoordinatesY:int, distFromSeed:Number, type:int, size:Number, limitRadius:int):void
		{
			var t:int = getTile(tileCoordinatesX, tileCoordinatesY);
			//just incase tile is not found
			if (t != -1)
			{
				if (_terrain.members[t].getTileType() == type) 
				{
					return;
				}
				_terrain.members[t].setTileType(stamp, type);
			}			
			if (distFromSeed >= limitRadius) { return;}
			//for each neighbour add water if lake depending on size of lake
			for (var ct:int = 0; ct < 8; ct++)
			{
				if (Math.random() < size/distFromSeed)
				{
					
					if (ct == 0) createRegion(tileCoordinatesX - 1, tileCoordinatesY, distFromSeed + 1, type, size, limitRadius);
					if(ct == 1) createRegion(tileCoordinatesX + 1, tileCoordinatesY, distFromSeed + 1, type, size, limitRadius);
					if(ct == 2) createRegion(tileCoordinatesX, tileCoordinatesY + 1, distFromSeed + 1, type, size, limitRadius);
					if(ct == 3) createRegion(tileCoordinatesX, tileCoordinatesY - 1, distFromSeed + 1, type, size, limitRadius);
				}
			}
		}
		
		/**
		 * Returns the index (within _terrain) of the tile at [tileCoordinatesX,tileCoordinatesY]
		 * Returns -1 if tile not found!
		 * 
		 * @param	tileCoordinatesX		The X-coordinate in tile space (not in pixels!).
		 * @param	tileCoordinatesY		The Y-coordinate in tile space (not in pixels!).
		 */
		public function getTile(tileCoordinatesX:int, tileCoordinatesY:int):int
		{
			for (var ct:int = 0; ct < _terrain.length; ct++)
			{
				if ((_terrain.members[ct].x-offsetX) / tileWidth == tileCoordinatesX && ((_terrain.members[ct].y-offsetY) / tileHeight == tileCoordinatesY))
				{
					return ct;
				}
			}
			return -1;
		}
		
		public function getTileType(tileCoordinatesX:int, tileCoordinatesY:int):int
		{
			var index:int = getTile(tileCoordinatesX, tileCoordinatesY);
			if (index == -1)
			{
				return -1;
			}
			else
			{
				return _terrain.members[index].getTileType();
			}
		}
		
		public function isTilePassable(tileCoordinatesX:int, tileCoordinatesY:int):Boolean
		{
			//check tile is on map to start with
			if (isTileOnMap(tileCoordinatesX, tileCoordinatesY))
			{
				if (!getTileType(tileCoordinatesX, tileCoordinatesY) == Tile.WATER)
				{ 
					return true; 
				}
			}
			
			return false;
		}
		
		private function isTileOnMap(tileCoordinatesX:int, tileCoordinatesY:int):Boolean
		{
			if (tileCoordinatesX < 0 || tileCoordinatesX >= mapWidth || tileCoordinatesY < 0 || tileCoordinatesY >= mapHeight)
			{
				return false;
			}
			return true;
		}
		
		public function destroy():void
		{
			_terrain.destroy();
		}
		
	}

}